import 'dart:async';
import 'dart:io';

import 'package:flut/src/assets/generator_assets.dart';
import 'package:flut/src/config/pubspec_config.dart';
import 'package:flut/src/generator/generator.dart';
import 'package:flut/src/utils/file_utils.dart';
import 'package:watcher/watcher.dart';
import 'package:flut/src/generator/generator_exception.dart';
import 'package:path/path.dart' as path;

/// The generator of initialization files.
class FlutWatch {
  late String projectName;

  /// Creates a new generator with configuration from the 'pubspec.yaml' file.
  FlutWatch() {
    final config = PubspecConfig();
    projectName = config.projectName;
  }

  /// Generates localization files.
  void watch() {
    var rootDirPath = getRootDirectoryPath();
    var assetsFolder = path.join(rootDirPath, 'assets');
    var i18nFolder = path.join(rootDirPath, 'lib', 'i18n');
    var entitiesFolder = path.join(rootDirPath, 'lib', 'entities');

    final watchTask = WatchTask()
        .addTarget(assetsFolder, _generatorAssets)
        .addTarget(i18nFolder, _generatorI18n)
        .addTarget(entitiesFolder, (event) {});

    /// 监听
    watchTask.watch();
    print('start watch');

    ProcessSignal.sigint.watch().listen((signal) async {
      await watchTask.cancel();
      print('Safe to exit');
      exit(0); // 程序正常结束
    });
  }

  Future<void> _generatorI18n(WatchEvent event) async {
    var startTime = DateTime.now();

    try {
      var generator = Generator();
      await generator.generateAsync();
    } on GeneratorException catch (e) {
      print(e.message);
    } catch (e) {
      print('Failed to generate localization files.\n$e');
    }

    print(
        'generatorI18n time: ${DateTime.now().difference(startTime).inMilliseconds}ms');
  }

  Future<void> _generatorAssets(WatchEvent event) async {
    var startTime = DateTime.now();

    try {
      var generator = GeneratorAssets();
      await generator.generateAsync();
    } on GeneratorException catch (e) {
      print(e.message);
    } catch (e) {
      print('Failed to generate assets files.\n$e');
    }

    print(
        '_generatorAssets time: ${DateTime.now().difference(startTime).inMilliseconds}ms');
  }
}

class WatchTask {
  List<String> folders = <String>[];
  List<void Function(WatchEvent event)?> onDatas = [];
  List<StreamSubscription<WatchEvent>?> streams = [];

  WatchTask();

  WatchTask addTarget(String folder, void Function(WatchEvent event)? onData) {
    folders.add(folder);
    onDatas.add(onData);
    return this;
  }

  /// 监控目录下文件变化
  WatchTask watch() {
    for (var i = 0; i < folders.length; i++) {
      final folder = folders[i];
      final onData = onDatas[i];
      _watchEach(folder, onData);
    }
    return this;
  }

  void _watchEach(String folder, void Function(WatchEvent event)? onData) {
    final watcher = DirectoryWatcher(folder);
    final stream = watcher.events.listen((event) {
      if (event is WatchEvent) {
        onData?.call(event);
      } else {
        print('File other: ${event.runtimeType} ${event.path}');
      }
    });
    streams.add(stream);
  }

  Future<void> cancel() async {
    for (var stream in streams) {
      await stream?.cancel();
    }
  }
}
